package ar.edu.itba.pod.legajo45129.simul.market;

import java.rmi.RemoteException;

import org.apache.log4j.Logger;

import ar.edu.itba.pod.legajo45129.simul.communication.ConnectionManagerImpl;
import ar.edu.itba.pod.legajo45129.simul.communication.factory.MessageFactory;
import ar.edu.itba.pod.simul.communication.Message;
import ar.edu.itba.pod.simul.local.LocalMarket;
import ar.edu.itba.pod.simul.market.Resource;
import ar.edu.itba.pod.simul.market.ResourceStock;

import com.google.common.collect.ConcurrentHashMultiset;
import com.google.common.collect.Multiset;

public class MyMarket extends LocalMarket {
	private static final Logger LOGGER = Logger.getLogger(MyMarket.class);
	private Multiset<Resource> sellingResourcesStock;
	private ConnectionManagerImpl connectionManager;
	
	public MyMarket(ConnectionManagerImpl connectionManager) {
		super();
		this.connectionManager = connectionManager;
		this.sellingResourcesStock = ConcurrentHashMultiset.create();
	}
	
	@Override
	protected void matchBothEnds() {
		for (ResourceStock buyer: this.buying) {
			for (ResourceStock seller: this.selling) {
				if (buyer.resource().equals(seller.resource())) {
					if (transfer(buyer, seller) != 0) {
						LOGGER.info("Selling Resources from here!");
					}
				}
			}
			if (this.buying.count(buyer) != 0) {
				for (Resource seller: this.sellingResourcesStock) {
					if (buyer.resource().equals(seller)) {
						if (transferIncResStock(buyer, seller) != 0) {
							LOGGER.info("Selling Resources from IncomingResources!");;
						}
					}
				}
				
				int amount = this.buying.count(buyer);
				if(amount != 0) {
					try {
						
						Message message = MessageFactory.ResourceRequestMessage(connectionManager.getNodeId().toString(), buyer.resource(), amount);
						this.connectionManager.getGroupCommunication().broadcast(message);
					} catch (RemoteException e) {
						LOGGER.error("RemoteException! Problem asking resources!");
					}
				}
			}
		}
	}
	
	protected int transferIncResStock (ResourceStock buyer, Resource seller) {
		while(true) {
			int wanted = this.buying.count(buyer);
			int available = this.sellingResourcesStock.count(seller);
			int transfer = Math.min(available, wanted);
			
			if (transfer == 0) {
				return 0;
			}
	
			boolean procured = this.sellingResourcesStock.setCount(seller, available, available - transfer);
			if (procured) {
				boolean sent = this.buying.setCount(buyer, wanted, wanted - transfer);
				if (sent) {
					try {
						buyer.add(transfer);
					}
					catch (RuntimeException e) {
						// market takes care of what was sold. 
						this.buying.remove(buyer, transfer);
						continue;
					}
					transactionCount++;
					return transfer;
				}
				else {
					// Compensation. restore what we took from the order!
					this.sellingResourcesStock.add(seller, transfer);
				}
			}
			// Reaching here mean we hit a race condition. Try again.
		}
	}
	
	public Multiset<ResourceStock> getSelling () {
		return this.selling;
	}
	
	public Multiset<ResourceStock> getBuying () {
		return this.buying;
	}
	
	public Multiset<Resource> getIncomingResourcesStock () {
		return this.sellingResourcesStock;
	}

	public int getStock(Resource resource, int amount) {
		for (ResourceStock stock: selling) {
			if (stock.resource().equals(resource)) {
				amount = Math.min(amount, selling.count(stock));
			}
		}

		return amount;
	}

	public void removeFromSelling(Resource resource, int amount) {		
		for (ResourceStock s: selling) {
			if (resource.equals(s.resource()) && selling.count(s) - amount >= 0) {
				selling.setCount(s, selling.count(s), selling.count(s) - amount);
				s.remove(amount);
				break;
			}
		}
		
	}
	
}